package com.tdu.spider.biz.service.same;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.tdu.spider.biz.service.same.thread.SameViewHisTask;
import com.tdu.spider.biz.service.same.vo.*;
import com.tdu.spider.biz.util.ConvertUtils;
import com.tdu.spider.dao.SameSenseLikeRepository;
import com.tdu.spider.dao.SameSenseRepository;
import com.tdu.spider.dao.SameUserRepository;
import com.tdu.spider.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Service
public class SameService {
    private static final Logger LOGGER = LoggerFactory.getLogger(SameService.class);

    @Autowired
    private SameApiService sameApiService;

    @Autowired
    private SameSenseRepository sameSenseRepository;

    @Autowired
    private SameSenseLikeRepository sameSenseLikeRepository;

    @Autowired
    private SameUserRepository sameUserRepository;

    @Autowired
    private SameChannelService sameChannelService;

    @Autowired
    private MongoTemplate mongoTemplate;

    private static LoadingCache<Long, SameUserDO> userDOLoadingCache = null;

    @PostConstruct
    public void init() {
        userDOLoadingCache = CacheBuilder.newBuilder().expireAfterAccess(4, TimeUnit.DAYS).build(new CacheLoader<Long, SameUserDO>() {
            @Override
            public SameUserDO load(Long key) throws Exception {
                return sameUserRepository.findById((key)).get();
            }
        });
    }

    public SameUserDO get(Long userId){
        try {
            return userDOLoadingCache.get(userId);
        } catch (Exception e) {
            return null;
        }
    }

    public SameUserDO getUserInfo(Long userId) throws Exception{
        SameUserDO sameUserDO = get(userId);
        if(sameUserDO==null){
            UserVO userVO = sameApiService.queryUserDetail(userId);
            if(userVO==null){
                return null;
            }
            sameUserDO= ConvertUtils.copy(userVO,SameUserDO.class);
            sameUserRepository.save(sameUserDO);
        }
        return sameUserDO;
    }

    public List<SameChannelDO> queryHotChannelInfo() throws Exception {
        long start=System.currentTimeMillis();
        List<SameChannelDO> sameChannelDOS= sameApiService.queryHotChannel();
        if(sameChannelDOS!=null){
            List<SameChannelDO> sameChannelDOS2= Lists.newArrayList();
            for (SameChannelDO sameChannelDO : sameChannelDOS) {
                UserInfoQuery query = new UserInfoQuery();
                query.setChannelId(sameChannelDO.getId());
                query.setUserId(15752075L);
                SameChannelDO channelDO =sameApiService. queryChannelDetail(query);
                if(channelDO!=null){
                    sameChannelDOS2.add(channelDO);
                }
            }
            LOGGER.info("queryHotChannelInfo ",System.currentTimeMillis()-start+"ms");
            return sameChannelDOS2;
        }
        return null;
    }

    public PageSenseVO getSenseByChannelId(Long channelId) throws Exception {
        UserInfoQuery userInfoQuery = new UserInfoQuery();
        userInfoQuery.setChannelId(channelId);
        PageSenseVO pageSenseVO = sameApiService.pageQueryChannelSense(userInfoQuery);
        return pageSenseVO;
    }


    public PageSenseVO pageQuerySense(SameSenseQueryVO sameSenseQueryVO) throws Exception {
        UserInfoQuery userInfoQuery = new UserInfoQuery();
        userInfoQuery.setOffset(sameSenseQueryVO.getOffset());

        PageSenseVO pageSenseVO = null;
        if (sameSenseQueryVO.getUserId() != null && sameSenseQueryVO.getChannelId() != null) {
            userInfoQuery.setUserId(sameSenseQueryVO.getUserId());
            userInfoQuery.setChannelId(sameSenseQueryVO.getChannelId());
            pageSenseVO = sameApiService.pageQueryChannelSense(userInfoQuery);
        } else {
            if (sameSenseQueryVO.getUserId() != null) {
                userInfoQuery.setUserId(sameSenseQueryVO.getUserId());
                pageSenseVO = sameApiService.pageQueryUserSense(userInfoQuery);
            }
            if (sameSenseQueryVO.getChannelId() != null) {
                userInfoQuery.setChannelId(sameSenseQueryVO.getChannelId());
                pageSenseVO = sameApiService.pageQueryChannelSense(userInfoQuery);
            }
        }
        return pageSenseVO;
    }

    private PageSenseVO repeatQuerySense(SameSenseQueryVO sameSenseQueryVO) {
        PageSenseVO pageSenseVO = null;
        //重试三次
        for (int j = 0; j < 3; j++) {
            try {
                pageSenseVO = pageQuerySense(sameSenseQueryVO);
            } catch (Exception e) {
                LOGGER.error("batchSpiderSense_page", e);
            }
            if (pageSenseVO != null) {
                return pageSenseVO;
            }
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                LOGGER.error("batchSpiderSense_sleep", e);
            }
        }
        return null;
    }


    public Boolean updateSpiderSense(SameSenseQueryVO sameSenseQueryVO) throws Exception {
        PageSenseVO pageSenseVO = null;
        long start = System.currentTimeMillis();
        Integer max = sameSenseQueryVO.getFilterLike();
        if (max == null) {
            max = 40;
        }
        List<SameSenseDO> senSeDOS = Lists.newArrayList();
        for (SameChannelDO channelDO : sameSenseQueryVO.getChannelInfoVOS()) {
            long start2 = System.currentTimeMillis();
            sameSenseQueryVO.setChannelId(channelDO.getId());

            int page=0;
            LOGGER.info("spider_run channelId:{} max:{}", sameSenseQueryVO.getChannelId(), max);
            while (true) {
                pageSenseVO = repeatQuerySense(sameSenseQueryVO);
                if (pageSenseVO == null || CollectionUtils.isEmpty(pageSenseVO.getResults())) {
                    LOGGER.error("spider channelId end cost:{}", (System.currentTimeMillis() - start2) / 100);
                    break;
                }
                LOGGER.info("spider_sense channelId:{},offset:{}", sameSenseQueryVO.getChannelId(), pageSenseVO.getOffset());

                sameSenseQueryVO.setOffset(pageSenseVO.getOffset());
                for (SenSeVO seVO : pageSenseVO.getResults()) {
                    if (seVO.getLikes() < max) {
                        continue;
                    }
                    SameSenseDO sameSenseDO = ConvertUtils.copy(seVO, SameSenseDO.class);
                    sameSenseDO.setUser(ConvertUtils.copy(seVO.getUser(), SameUserDO.class));
                    if (seVO.getMedia() != null && seVO.getMedia().getVideo() != null) {
                        sameSenseDO.setMedis_src(seVO.getMedia().getVideo().getSrc());
                    }
                    sameSenseDO.setChannel(seVO.getChannel());
                    senSeDOS.add(sameSenseDO);
                }

                for(int i=0;i<3;i++) {
                    try {
                        sameSenseRepository.saveAll(senSeDOS);
                        break;
                    } catch (Exception e) {
                    }
                }

                senSeDOS.clear();
                TimeUnit.SECONDS.sleep(2L);
                page++;
                if(sameSenseQueryVO.getFilterPage()!=null && sameSenseQueryVO.getFilterPage()<=page){
                    LOGGER.error("spider channelId page_end:{} cost:{}",sameSenseQueryVO.getFilterPage(), (System.currentTimeMillis() - start2) / 100);
                    break;
                }
            }
            sameSenseQueryVO.setOffset(null);
            TimeUnit.MINUTES.sleep(1L);
            LOGGER.info("updateSpiderSense_channel success cost:{},channelId:{}", (System.currentTimeMillis() - start2) / 100, sameSenseQueryVO.getChannelId());
        }
        LOGGER.info("updateSpiderSense_all success cost:{},channelCount:{}", (System.currentTimeMillis() - start) / 100, sameSenseQueryVO.getChannelId(), sameSenseQueryVO.getChannelInfoVOS().size());
        return true;
    }


    public Boolean daliyUpdateDefaultChannelSense(SameSenseQueryVO sameSenseQueryVO) throws Exception {
        long start = System.currentTimeMillis();
        List<SameChannelDO> channelInfoVOS = sameChannelService.getAll();
        sameSenseQueryVO.setChannelInfoVOS(channelInfoVOS);

        if (sameSenseQueryVO.getChannelId() != null) {
            sameSenseQueryVO.setChannelInfoVOS(Lists.newArrayList(SameChannelDO.build(sameSenseQueryVO.getChannelId(), "单独")));
        }
        updateSpiderSense(sameSenseQueryVO);
        LOGGER.info("daliyUpdateDefaultChannelSense update success:{},cost:{}" + channelInfoVOS.size(), (System.currentTimeMillis() - start) / 1000);
        return true;
    }


    public Boolean daliyUpdateActivityChannelSense(SameSenseQueryVO sameSenseQueryVO) throws Exception {
        long start = System.currentTimeMillis();
        UserInfoQuery query = new UserInfoQuery();
        query.setUserId(sameSenseQueryVO.getUserId());

        Integer max = sameSenseQueryVO.getFilterLike();
        if (max == null) {
            max = 40;
        }

        //step1:获得活跃频道
        List<RecommendationResultVO> recommendationResultVOS = sameApiService.recommendationChannel(query);
        if (CollectionUtils.isEmpty(recommendationResultVOS)) {
            return false;
        }
        for (RecommendationResultVO recommendationResultVO : recommendationResultVOS) {
            List<SameSenseDO> senSeDOS = Lists.newArrayList();
            List<SenSeVO> sense_data = recommendationResultVO.getChannel().getSense_data();
            for (SenSeVO seVO : sense_data) {
                if (seVO.getLikes() < max) {
                    continue;
                }
                SameSenseDO sameSenseDO = ConvertUtils.copy(seVO, SameSenseDO.class);
                sameSenseDO.setUser(ConvertUtils.copy(seVO.getUser(), SameUserDO.class));
                if (seVO.getMedia() != null && seVO.getMedia().getVideo() != null) {
                    sameSenseDO.setMedis_src(seVO.getMedia().getVideo().getSrc());
                }
                sameSenseDO.setChannel(seVO.getChannel());
                senSeDOS.add(sameSenseDO);
            }
            sameSenseRepository.saveAll(senSeDOS);
        }

        LOGGER.info("daliyUpdateActivityChannelSense update success:{},cost:{}" + recommendationResultVOS.size(), (System.currentTimeMillis() - start) / 1000);
        return true;
    }


    public Boolean likeUpdateTopSense(LikeSenseQueryVO likeSenseQueryVO) throws Exception {
        long start = System.currentTimeMillis();
        SameSenseQueryVO sameSenseQueryVO = new SameSenseQueryVO();
        sameSenseQueryVO.setChannelInfoVOS(likeSenseQueryVO.getChannelInfoVOS());
        sameSenseQueryVO.setUserId(likeSenseQueryVO.getUserId());
        likeSameSense(sameSenseQueryVO);

        LOGGER.info("likeUpdateTopSense update success:{},cost:{}" ,sameSenseQueryVO.getChannelInfoVOS().size(), (System.currentTimeMillis() - start) / 1000);
        return true;
    }

    public void likeSameSense(SameSenseQueryVO sameSenseQueryVO) throws Exception {
        for (SameChannelDO channelDO : sameSenseQueryVO.getChannelInfoVOS()) {
            long start = System.currentTimeMillis();
            sameSenseQueryVO.setChannelId(channelDO.getId());
            PageSenseVO pageSenseVO = repeatQuerySense(sameSenseQueryVO);
            if (pageSenseVO == null && CollectionUtils.isEmpty(pageSenseVO.getResults())) {
                return;
            }
            LOGGER.info("getCount:{},name:{}", pageSenseVO.getResults().size(), channelDO.getName());

            //增加随即点赞
            List<SenSeVO> sense_data = pageSenseVO.getResults();
            Collections.shuffle(sense_data);
            sense_data = sense_data.subList(0, 3);

            for (SenSeVO seVO : sense_data) {
                TimeUnit.SECONDS.sleep(1);
                UserInfoQuery userInfoQuery = new UserInfoQuery();
                userInfoQuery.setChannelId(sameSenseQueryVO.getChannelId());
                userInfoQuery.setSenseId(seVO.getId());
                userInfoQuery.setUserId(sameSenseQueryVO.getUserId());
                sameApiService.likeSense(userInfoQuery);
            }
            LOGGER.info("likeSameSense success cost:{},channelId:{},name:{}", (System.currentTimeMillis() - start) / 100, channelDO.getId(), channelDO.getName());
        }
    }


    public Boolean randomActivityLikeSense(LikeSenseQueryVO likeSenseQueryVO) throws Exception {
        long start = System.currentTimeMillis();
        UserInfoQuery query = new UserInfoQuery();
        query.setUserId(likeSenseQueryVO.getUserId());
        //step1:获得活跃频道
        List<RecommendationResultVO> recommendationResultVOS = sameApiService.recommendationChannel(query);
        if (CollectionUtils.isEmpty(recommendationResultVOS)) {
            return false;
        }
        for (RecommendationResultVO recommendationResultVO : recommendationResultVOS) {
            if (recommendationResultVO.getChannel() == null || CollectionUtils.isEmpty(recommendationResultVO.getChannel().getSense_data())) {
                continue;
            }
            //step2:增加随即点赞
            List<SenSeVO> sense_data = recommendationResultVO.getChannel().getSense_data();
            Collections.shuffle(sense_data);
            if (sense_data.size() > likeSenseQueryVO.getFilterLikes()) {
                sense_data = sense_data.subList(0, likeSenseQueryVO.getFilterLikes());
            }
            for (SenSeVO seVO : sense_data) {
                if (Lists.newArrayList(1403224L, 590L, 1056740L, 1244040L).contains(seVO.getChannel_id())) {
                    continue;
                }
                TimeUnit.SECONDS.sleep(1);

                UserInfoQuery userInfoQuery = new UserInfoQuery();
                userInfoQuery.setChannelId(seVO.getChannel_id());
                userInfoQuery.setSenseId(seVO.getId());
                userInfoQuery.setUserId(likeSenseQueryVO.getUserId());
                sameApiService.likeSense(userInfoQuery);
            }
        }

        LOGGER.info("randomActivityLikeSense update userId:{},cost:{}", likeSenseQueryVO.getUserId(), (System.currentTimeMillis() - start) / 1000);
        return true;
    }


    public Boolean viewUserSense(LikeSenseQueryVO likeSenseQueryVO) throws Exception {
        long start = System.currentTimeMillis();
        UserInfoQuery userInfoQuery = new UserInfoQuery();
        userInfoQuery.setUserId(likeSenseQueryVO.getUserId());
        PageSenseVO sense = sameApiService.pageQueryUserSense(userInfoQuery);
        if (sense == null && CollectionUtils.isEmpty(sense.getResults())) {
            return false;
        }

        //step2:增加随即查看
        List<SenSeVO> sense_data = sense.getResults();
        Collections.shuffle(sense_data);
        if (sense_data.size() > likeSenseQueryVO.getFilterLikes()) {
            sense_data = sense_data.subList(0, likeSenseQueryVO.getFilterLikes());
        }
        List<Long> senseIds=Lists.newArrayList();
        for (SenSeVO seVO : sense_data) {
            senseIds.add(seVO.getId());
        }
        userInfoQuery.setSenseIds(senseIds);
        sameApiService.viewSense(userInfoQuery);
        LOGGER.info("viewUserSense update userId:{},cost:{}", likeSenseQueryVO.getUserId(), (System.currentTimeMillis() - start) / 1000);
        return false;
    }


    public Boolean saveUserInfo(Long userId) throws Exception {
        long start = System.currentTimeMillis();
        UserVO userVO = sameApiService.queryUserDetail(userId);
        if (userVO == null) {
            return null;
        }
        SameUserDO sameUserDO = ConvertUtils.copy(userVO, SameUserDO.class);
        if (userVO.getMeta() != null) {
            UserMetaDO meta = ConvertUtils.copy(userVO.getMeta(), UserMetaDO.class);
            sameUserDO.setMeta(meta);
        }
        sameUserRepository.save(sameUserDO);
        LOGGER.info("saveUserInfo update userId:{},cost:{}", userId, (System.currentTimeMillis() - start) / 1000);
        return true;
    }

    public void spiderUserSenseAndLikes(SameAnyQueryVO sameAnyQueryVO) throws Exception {
        long start = System.currentTimeMillis();
        //step1:采集到用户的所有数据
        PageSenseVO pageSenseVO = null;
        SameSenseQueryVO sameSenseQueryVO = new SameSenseQueryVO();
        sameSenseQueryVO.setUserId(sameAnyQueryVO.getUserId());
//        sameSenseQueryVO.setOffset(1489413469L);
        List<Long> idList=Lists.newArrayList();
        int total = 0;
        LOGGER.info("统计2017_spider_start userId:{}", sameSenseQueryVO.getUserId());

        while (true) {
            if(pageSenseVO!=null && pageSenseVO.getOffset()==-1){
                LOGGER.error("统计2017_spider total:{} end cost:{}", total, (System.currentTimeMillis() - start) / 100);
                break;
            }

            List<SameSenseDO> senSeDOS = Lists.newArrayList();
            pageSenseVO = repeatQuerySense(sameSenseQueryVO);
            if (pageSenseVO == null || CollectionUtils.isEmpty(pageSenseVO.getResults())) {
                LOGGER.error("统计2017_spider total:{} end cost:{}", total, (System.currentTimeMillis() - start) / 100);
                break;
            }

            LOGGER.info("统计2017_spider_sense channelId:{},offset:{}", sameSenseQueryVO.getChannelId(), pageSenseVO.getOffset());

            sameSenseQueryVO.setOffset(pageSenseVO.getOffset());
            for (SenSeVO seVO : pageSenseVO.getResults()) {
                SameSenseDO sameSenseDO = ConvertUtils.copy(seVO, SameSenseDO.class);
                sameSenseDO.setUser(ConvertUtils.copy(seVO.getUser(), SameUserDO.class));
                if (seVO.getMedia() != null && seVO.getMedia().getVideo() != null) {
                    sameSenseDO.setMedis_src(seVO.getMedia().getVideo().getSrc());
                }
                sameSenseDO.setChannel(seVO.getChannel());
                senSeDOS.add(sameSenseDO);
                idList.add(seVO.getId());
            }
            total += senSeDOS.size();
            sameSenseRepository.saveAll(senSeDOS);
        }
        LOGGER.info("统计2017 update params:{},cost:{}", sameAnyQueryVO, (System.currentTimeMillis() - start) / 1000);

        //是否需要浏览记录
        if(sameAnyQueryVO.getGetLikes()) {
            //step2:删除评价数据
            Query query = Query.query(Criteria.where("targetUserId").is(sameAnyQueryVO.getUserId()));
            mongoTemplate.remove(query, "same_sense_like");

            //step3:开启多线程抓取评论
            List<ListenableFuture<List<SenseLikeDO>>> futures = Lists.newArrayList();
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0,100, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
            ListeningExecutorService executorService = MoreExecutors.listeningDecorator(threadPoolExecutor);
            for (int i = 0; i < idList.size(); i++) {
                futures.add(executorService.submit(new SameViewHisTask(sameApiService, sameSenseLikeRepository, idList.get(i), sameAnyQueryVO.getUserId())));
            }
            final ListenableFuture<List<List<SenseLikeDO>>> resultsFuture = Futures.successfulAsList(futures);
            List<List<SenseLikeDO>> listList = resultsFuture.get();
            int size = 0;
            for (List<SenseLikeDO> senseLikeDOS : listList) {
                size += senseLikeDOS.size();
                sameSenseLikeRepository.saveAll(senseLikeDOS);
            }
            LOGGER.info("统计2017_END size:{}",size);
        }
    }
}
